#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
服务控制器
负责管理后台API服务的启动、停止、状态监控等功能
"""

import os
import sys
import time
import psutil
import signal
import logging
import subprocess
import threading
from typing import Dict, Any, Optional, Callable
from datetime import datetime


class ServiceController:
    """服务控制器"""

    def __init__(self, config_manager):
        """
        初始化服务控制器

        Args:
            config_manager: 配置管理器实例
        """
        self.config = config_manager
        self.logger = logging.getLogger(__name__)

        # 服务进程信息
        self.service_process = None
        self.service_pid = None
        self.service_status = "stopped"  # stopped, starting, running, stopping
        self.service_start_time = None  # 服务启动时间

        # 状态监控线程
        self.monitor_thread = None
        self.monitoring = False

        # 状态回调函数
        self.status_callback = None

    def set_status_callback(self, callback: Callable[[str, Dict[str, Any]], None]):
        """设置状态变化回调函数"""
        self.status_callback = callback

    def _notify_status_change(self, status: str, details: Dict[str, Any] = None):
        """通知状态变化"""
        self.service_status = status
        if self.status_callback:
            self.status_callback(status, details or {})

    def start_service(self) -> Dict[str, Any]:
        """启动API服务"""
        result = {
            "success": False,
            "message": "",
            "pid": None,
            "timestamp": datetime.now().isoformat()
        }

        try:
            # 检查是否已经运行
            if self.is_service_running():
                result["message"] = "服务已在运行中"
                result["success"] = True
                result["pid"] = self.service_pid
                return result

            self.logger.info("正在启动API服务...")
            self._notify_status_change("starting", {"message": "正在启动服务"})

            # 获取服务配置
            service_config = self.config.get_service_config()
            host = service_config.get('host', '0.0.0.0')
            port = service_config.get('port', 5000)

            # 检查端口是否被占用
            if self._is_port_in_use(port):
                # 获取占用端口的进程信息
                occupying_pid = self._get_port_occupying_process(port)
                if occupying_pid:
                    error_msg = f"端口 {port} 已被进程 {occupying_pid} 占用。请先停止该进程或使用其他端口。"
                    self.logger.error(f"端口 {port} 被PID {occupying_pid} 占用")
                else:
                    error_msg = f"端口 {port} 已被占用，无法获取占用进程信息。"
                    self.logger.error(f"端口 {port} 被未知进程占用")

                result["message"] = error_msg
                # 清理状态
                self.service_process = None
                self.service_pid = None
                self._notify_status_change("stopped", {"error": error_msg})
                return result

            # 构建启动命令
            python_exe = sys.executable
            script_dir = os.path.dirname(os.path.abspath(__file__))
            service_script = os.path.join(script_dir, "draft_automation_service.py")

            if not os.path.exists(service_script):
                error_msg = f"服务脚本不存在: {service_script}"
                result["message"] = error_msg
                self.logger.error(error_msg)
                # 清理状态
                self.service_process = None
                self.service_pid = None
                self._notify_status_change("stopped", {"error": error_msg})
                return result

            # 尝试两种启动方式：先尝试完全分离，失败则用系统命令
            try:
                # 方法1：使用完全分离的进程标志
                cmd = [python_exe, service_script]

                creation_flags = 0
                if sys.platform == "win32":
                    creation_flags = (
                        0x00000008 |  # DETACHED_PROCESS - 完全分离的进程
                        0x00000200 |  # CREATE_NEW_PROCESS_GROUP - 新进程组
                        0x08000000     # CREATE_NO_WINDOW - 不创建控制台窗口
                    )

                self.service_process = subprocess.Popen(
                    cmd,
                    cwd=script_dir,
                    stdout=subprocess.DEVNULL,
                    stderr=subprocess.DEVNULL,
                    stdin=subprocess.DEVNULL,
                    close_fds=True,
                    creationflags=creation_flags
                )

                self.logger.info(f"使用分离进程方式启动API服务，PID: {self.service_process.pid}")

            except Exception as e:
                self.logger.warning(f"分离进程方式启动失败: {e}，尝试系统命令方式")

                # 方法2：使用系统命令启动（完全独立）
                if sys.platform == "win32":
                    # Windows: 使用start命令启动独立进程
                    cmd_str = f'start "API Service" /B "{python_exe}" "{service_script}"'
                    self.service_process = subprocess.Popen(
                        cmd_str,
                        shell=True,
                        cwd=script_dir,
                        stdout=subprocess.DEVNULL,
                        stderr=subprocess.DEVNULL
                    )
                else:
                    # Unix/Linux: 使用nohup启动独立进程
                    cmd_str = f'nohup "{python_exe}" "{service_script}" > /dev/null 2>&1 &'
                    self.service_process = subprocess.Popen(
                        cmd_str,
                        shell=True,
                        cwd=script_dir
                    )

                self.logger.info(f"使用系统命令方式启动API服务，PID: {self.service_process.pid}")

            self.service_pid = self.service_process.pid

            self.logger.info(f"API服务已启动为分离进程，PID: {self.service_pid}")
            self.logger.info("进程完全分离，解决COM对象线程冲突问题")

            # 启动验证：检查进程启动并等待服务就绪
            import time
            time.sleep(1)  # 给进程一点启动时间

            # 检查进程是否成功启动
            process_started = False
            if self.service_process:
                if self.service_process.poll() is None:
                    process_started = True
                else:
                    # 进程可能是通过start命令启动的，需要另外验证
                    if self.service_pid:
                        try:
                            import psutil
                            if psutil.Process(self.service_pid).is_running():
                                process_started = True
                        except:
                            pass

            if process_started:
                # 记录启动时间
                self.service_start_time = time.time()

                result["success"] = True
                result["message"] = f"服务进程启动成功，PID: {self.service_pid}"
                result["pid"] = self.service_pid

                self.logger.info(f"API服务进程启动成功，PID: {self.service_pid}")
                self._notify_status_change("starting", {
                    "pid": self.service_pid,
                    "host": host,
                    "port": port,
                    "message": "服务进程已启动，正在初始化..."
                })

                # 启动异步验证线程，检查服务是否真正就绪
                self._start_service_readiness_check(host, port)

            else:
                error_msg = "服务进程启动失败"
                result["message"] = error_msg
                self.logger.error(error_msg)
                self._stop_service_process()
                # 清理状态
                self.service_process = None
                self.service_pid = None
                self._notify_status_change("stopped", {"error": error_msg})

        except Exception as e:
            error_msg = f"启动服务失败: {str(e)}"
            result["message"] = error_msg
            self.logger.error(error_msg)
            # 清理状态
            self.service_process = None
            self.service_pid = None
            self._notify_status_change("stopped", {"error": error_msg})

        return result

    def _start_service_readiness_check(self, host: str, port: int):
        """启动异步服务就绪检查"""
        def check_readiness():
            max_attempts = 30  # 最多等待30秒
            attempt = 0

            while attempt < max_attempts:
                try:
                    # 检查进程是否还在运行
                    if not self.is_service_running_fast():
                        self.logger.error("服务进程在启动过程中退出")
                        self._notify_status_change("stopped", {
                            "message": "服务进程在启动过程中退出",
                            "unexpected": True
                        })
                        return

                    # 尝试连接API服务
                    try:
                        import requests
                        response = requests.get(f"http://localhost:{port}/api/status", timeout=2)
                        if response.status_code == 200:
                            self.logger.info(f"API服务启动成功并就绪，端口: {port}")
                            self._notify_status_change("running", {
                                "pid": self.service_pid,
                                "host": host,
                                "port": port,
                                "message": f"服务运行正常，端口: {port}"
                            })
                            # 启动常规监控
                            self._start_monitoring()
                            return

                    except ImportError:
                        # 没有requests库，仅依靠进程检查
                        if attempt > 10:  # 10秒后认为就绪
                            self.logger.info("API服务进程启动成功（无requests库，跳过HTTP检查）")
                            self._notify_status_change("running", {
                                "pid": self.service_pid,
                                "host": host,
                                "port": port,
                                "message": "服务进程运行中（跳过HTTP检查）"
                            })
                            self._start_monitoring()
                            return

                    except Exception as e:
                        # 连接失败，继续等待
                        self.logger.debug(f"等待服务就绪... 尝试 {attempt + 1}/{max_attempts}: {e}")

                    attempt += 1
                    time.sleep(1)

                except Exception as e:
                    self.logger.error(f"服务就绪检查异常: {e}")
                    break

            # 超时或出错
            self.logger.warning("服务启动超时或检查失败，但进程仍在运行")
            self._notify_status_change("running", {
                "pid": self.service_pid,
                "host": host,
                "port": port,
                "message": "服务进程运行中（启动检查超时）"
            })
            self._start_monitoring()

        # 在后台线程中执行检查
        threading.Thread(target=check_readiness, daemon=True).start()

    def stop_service(self) -> Dict[str, Any]:
        """停止API服务"""
        result = {
            "success": False,
            "message": "",
            "timestamp": datetime.now().isoformat()
        }

        try:
            if not self.is_service_running():
                result["message"] = "服务未在运行"
                result["success"] = True
                return result

            self.logger.info("正在停止API服务...")
            self._notify_status_change("stopping", {"message": "正在停止服务"})

            # 停止监控
            self._stop_monitoring()

            # 停止服务进程
            if self._stop_service_process():
                result["success"] = True
                result["message"] = "服务停止成功"
                self.logger.info("API服务停止成功")
                self._notify_status_change("stopped", {"message": "服务已停止"})
            else:
                error_msg = "服务停止失败"
                result["message"] = error_msg
                self.logger.error(error_msg)

        except Exception as e:
            error_msg = f"停止服务失败: {str(e)}"
            result["message"] = error_msg
            self.logger.error(error_msg)

        return result

    def restart_service(self) -> Dict[str, Any]:
        """重启API服务"""
        self.logger.info("正在重启API服务...")

        # 先停止服务
        stop_result = self.stop_service()
        if not stop_result["success"]:
            return stop_result

        # 等待一段时间
        time.sleep(2)

        # 再启动服务
        return self.start_service()

    def get_service_status(self) -> Dict[str, Any]:
        """获取服务状态"""
        running = self.is_service_running()
        current_time = time.time()

        # 智能状态判断：考虑启动保护期
        if self.service_status == "starting" and self.service_start_time:
            # 启动后30秒内保持starting状态，避免频繁检查干扰服务初始化
            if current_time - self.service_start_time < 30:
                # 仍在启动保护期内
                pass
            elif running:
                # 保护期结束且进程正在运行，切换到running状态
                self.service_status = "running"
            else:
                # 保护期结束但进程不在运行，切换到stopped状态
                self.service_status = "stopped"
        elif running and self.service_status == "stopped":
            self.service_status = "running"
        elif not running and self.service_status in ["running", "starting"]:
            self.service_status = "stopped"

        status_info = {
            "status": self.service_status,
            "running": running,
            "pid": self.service_pid,
            "uptime": None,
            "memory_usage": None,
            "cpu_usage": None,
            "port": self.config.get('service.port', 5000),
            "host": self.config.get('service.host', '0.0.0.0'),
            "timestamp": datetime.now().isoformat()
        }

        # 如果服务正在运行，获取详细信息
        if running and self.service_pid:
            try:
                process = psutil.Process(self.service_pid)
                status_info["uptime"] = time.time() - process.create_time()
                status_info["memory_usage"] = process.memory_info().rss / 1024 / 1024  # MB
                status_info["cpu_usage"] = process.cpu_percent()
            except (psutil.NoSuchProcess, psutil.AccessDenied):
                pass

        return status_info

    def get_service_status_fast(self) -> Dict[str, Any]:
        """获取服务状态（快速版本，仅检查进程状态，不进行网络调用）"""
        running = self.is_service_running_fast()
        current_time = time.time()

        # 智能状态判断：考虑启动保护期
        if self.service_status == "starting" and self.service_start_time:
            # 启动后60秒内保持starting状态，避免频繁检查干扰服务初始化
            if current_time - self.service_start_time < 60:
                # 仍在启动保护期内
                pass
            elif running:
                # 保护期结束且进程正在运行，切换到running状态
                self.service_status = "running"
            else:
                # 保护期结束但进程不在运行，切换到stopped状态
                self.service_status = "stopped"
        elif running and self.service_status == "stopped":
            self.service_status = "running"
        elif not running and self.service_status in ["running", "starting"]:
            self.service_status = "stopped"

        status_info = {
            "status": self.service_status,
            "running": running,
            "pid": self.service_pid,
            "uptime": None,
            "memory_usage": None,
            "cpu_usage": None,
            "port": self.config.get('service.port', 5000),
            "host": self.config.get('service.host', '0.0.0.0'),
            "timestamp": datetime.now().isoformat()
        }

        # 如果服务正在运行，获取详细信息（仅进程信息，不调用网络）
        if running and self.service_pid:
            try:
                process = psutil.Process(self.service_pid)
                status_info["uptime"] = time.time() - process.create_time()
                status_info["memory_usage"] = process.memory_info().rss / 1024 / 1024  # MB
                status_info["cpu_usage"] = process.cpu_percent()
            except (psutil.NoSuchProcess, psutil.AccessDenied):
                pass

        return status_info

    def is_service_running_fast(self) -> bool:
        """快速检查服务是否正在运行（仅检查进程，不进行网络调用）"""
        # 首先检查记录的进程是否还在运行
        if self.service_process and self.service_pid:
            try:
                process = psutil.Process(self.service_pid)
                if process.is_running():
                    return True
                else:
                    self.service_process = None
                    self.service_pid = None
                    return False
            except (psutil.NoSuchProcess, psutil.AccessDenied):
                self.service_process = None
                self.service_pid = None
                return False

        # 如果没有记录的进程，检查端口是否被占用
        service_config = self.config.get_service_config()
        port = service_config.get('port', 5000)

        if self._is_port_in_use(port):
            # 端口被占用，尝试获取占用进程的PID
            pid = self._get_port_occupying_process(port)
            if pid:
                try:
                    self.service_pid = pid
                    self.service_process = psutil.Process(pid)
                    return True
                except:
                    pass
            return True  # 端口被占用，假设服务在运行

        return False

    def is_service_running(self) -> bool:
        """检查服务是否正在运行（轻量级检查，不调用健康检查API）"""
        # 首先检查记录的进程是否还在运行
        if self.service_process and self.service_pid:
            try:
                process = psutil.Process(self.service_pid)
                if process.is_running():
                    return True
                else:
                    self.service_process = None
                    self.service_pid = None
                    return False
            except (psutil.NoSuchProcess, psutil.AccessDenied):
                self.service_process = None
                self.service_pid = None
                return False

        # 如果没有记录的进程，检查端口是否被占用
        service_config = self.config.get_service_config()
        port = service_config.get('port', 5000)

        if self._is_port_in_use(port):
            # 端口被占用，尝试获取占用进程的PID
            pid = self._get_port_occupying_process(port)
            if pid:
                try:
                    self.service_pid = pid
                    self.service_process = psutil.Process(pid)
                    return True
                except:
                    pass
            return True  # 端口被占用，假设服务在运行

        return False

    def _check_service_health(self) -> bool:
        """检查服务健康状态（轻量级，避免触发UI自动化）"""
        try:
            import requests
            service_config = self.config.get_service_config()
            port = service_config.get('port', 5000)

            # 使用轻量级状态检查，完全避免UI自动化冲突
            response = requests.get(f"http://localhost:{port}/api/status", timeout=2)
            return response.status_code == 200
        except:
            return False

    def _wait_for_service_start(self, timeout: float = 30) -> bool:
        """等待服务启动"""
        start_time = time.time()

        while time.time() - start_time < timeout:
            if self._check_service_health():
                return True

            # 检查进程是否还在运行
            if self.service_process and self.service_process.poll() is not None:
                # 进程已退出
                return False

            time.sleep(1)

        return False

    def _stop_service_process(self) -> bool:
        """停止服务进程（处理分离进程）"""
        try:
            if self.service_pid:
                try:
                    # 使用psutil来更好地管理分离的进程
                    import psutil
                    process = psutil.Process(self.service_pid)

                    if process.is_running():
                        self.logger.info(f"正在停止分离进程 PID: {self.service_pid}")

                        # 尝试优雅停止
                        process.terminate()

                        # 等待进程结束
                        try:
                            process.wait(timeout=10)
                            self.logger.info(f"分离进程已优雅停止 PID: {self.service_pid}")
                        except psutil.TimeoutExpired:
                            # 强制杀死进程
                            self.logger.warning(f"强制停止分离进程 PID: {self.service_pid}")
                            process.kill()
                            process.wait(timeout=5)

                except (psutil.NoSuchProcess, psutil.AccessDenied):
                    self.logger.info(f"分离进程已不存在 PID: {self.service_pid}")
                except ImportError:
                    # 如果没有psutil，使用原来的方法
                    if self.service_process:
                        if sys.platform == "win32":
                            self.service_process.send_signal(signal.CTRL_BREAK_EVENT)
                        else:
                            self.service_process.terminate()

                        try:
                            self.service_process.wait(timeout=10)
                        except subprocess.TimeoutExpired:
                            self.service_process.kill()
                            self.service_process.wait()

            # 清理状态
            self.service_process = None
            self.service_pid = None
            self.service_start_time = None
            return True

        except Exception as e:
            self.logger.error(f"停止服务进程失败: {e}")
            # 强制清理状态
            self.service_process = None
            self.service_pid = None
            self.service_start_time = None
            return False

    def _is_port_in_use(self, port: int) -> bool:
        """检查端口是否被占用"""
        try:
            for conn in psutil.net_connections():
                if conn.laddr.port == port and conn.status == psutil.CONN_LISTEN:
                    return True
            return False
        except:
            return False

    def _get_port_occupying_process(self, port: int) -> Optional[int]:
        """获取占用指定端口的进程PID"""
        try:
            for conn in psutil.net_connections():
                if conn.laddr.port == port and conn.status == psutil.CONN_LISTEN:
                    return conn.pid
            return None
        except:
            return None

    def _start_monitoring(self):
        """启动状态监控（避免重复启动）"""
        if not self.monitoring:
            self.monitoring = True
            self.monitor_thread = threading.Thread(target=self._monitor_service, daemon=True)
            self.monitor_thread.start()
            self.logger.debug("服务监控线程已启动")
        else:
            self.logger.debug("服务监控线程已在运行，跳过重复启动")

    def _stop_monitoring(self):
        """停止状态监控"""
        self.monitoring = False
        if self.monitor_thread:
            self.monitor_thread.join(timeout=5)

    def _monitor_service(self):
        """监控分离进程状态（轻量级，智能频率）"""
        consecutive_failures = 0
        max_failures = 3
        check_count = 0

        self.logger.debug("开始监控分离进程状态")

        # 初始等待时间较短，让GUI能快速响应状态变化
        time.sleep(5)

        while self.monitoring:
            try:
                if self.service_pid:
                    # 使用psutil检查分离进程状态
                    try:
                        import psutil
                        process = psutil.Process(self.service_pid)
                        if not process.is_running():
                            consecutive_failures += 1
                            self.logger.warning(f"分离进程检查失败 {consecutive_failures}/{max_failures} PID: {self.service_pid}")

                            if consecutive_failures >= max_failures:
                                self.logger.warning(f"分离进程已停止 PID: {self.service_pid}")
                                self.service_process = None
                                self.service_pid = None
                                self._notify_status_change("stopped", {
                                    "message": "分离进程意外停止",
                                    "unexpected": True
                                })
                                break
                        else:
                            consecutive_failures = 0
                            check_count += 1
                            # 定期记录状态，但不要太频繁
                            if check_count % 10 == 0:  # 每10次检查记录一次
                                self.logger.debug(f"分离进程运行正常 PID: {self.service_pid}")

                    except ImportError:
                        # 没有psutil，使用基本检查
                        if self.service_process and self.service_process.poll() is not None:
                            consecutive_failures += 1
                            if consecutive_failures >= max_failures:
                                self.logger.warning("服务进程已退出")
                                self.service_process = None
                                self.service_pid = None
                                self._notify_status_change("stopped", {
                                    "message": "服务进程意外退出",
                                    "unexpected": True
                                })
                                break
                        else:
                            consecutive_failures = 0

                    except (psutil.NoSuchProcess, psutil.AccessDenied):
                        consecutive_failures += 1
                        if consecutive_failures >= max_failures:
                            self.logger.warning(f"分离进程不存在或无权限访问 PID: {self.service_pid}")
                            self.service_process = None
                            self.service_pid = None
                            self._notify_status_change("stopped", {
                                "message": "分离进程不存在或无权限访问",
                                "unexpected": True
                            })
                            break

                # 智能监控频率：前几次检查较频繁，之后降低频率
                if check_count < 5:
                    sleep_time = 10  # 前5次每10秒检查
                elif check_count < 20:
                    sleep_time = 30  # 接下来15次每30秒检查
                else:
                    sleep_time = 60  # 之后每60秒检查

                time.sleep(sleep_time)

            except Exception as e:
                self.logger.error(f"监控分离进程异常: {e}")
                time.sleep(60)

        self.logger.debug("分离进程监控线程结束")

    def get_service_logs(self, lines: int = 100) -> list:
        """获取服务日志"""
        logs = []
        try:
            if self.service_process:
                # 这里可以实现日志读取逻辑
                # 由于我们的服务输出到stdout/stderr，可以从那里读取
                pass
        except Exception as e:
            self.logger.error(f"获取服务日志失败: {e}")

        return logs

    def test_service_connection(self) -> Dict[str, Any]:
        """测试服务连接（超轻量级，优先使用进程检查，避免网络调用）"""
        result = {
            "success": False,
            "message": "",
            "response_time": None,
            "timestamp": datetime.now().isoformat()
        }

        try:
            # 首先进行快速进程检查
            if not self.is_service_running_fast():
                result["message"] = "服务进程未运行"
                return result

            # 如果在启动保护期内，仅返回进程状态
            if self.service_start_time and (time.time() - self.service_start_time < 30):
                result["success"] = True
                result["message"] = "服务进程运行中（启动保护期，跳过网络检查）"
                result["response_time"] = 0
                return result

            # 尝试轻量级网络检查（可选）
            try:
                import requests
                service_config = self.config.get_service_config()
                port = service_config.get('port', 5000)

                start_time = time.time()
                # 使用极短超时的专用状态端点，避免阻塞
                response = requests.get(f"http://localhost:{port}/api/status", timeout=1)
                response_time = time.time() - start_time

                if response.status_code == 200:
                    result["success"] = True
                    result["message"] = "服务连接正常"
                    result["response_time"] = round(response_time * 1000, 2)  # ms
                else:
                    # 即使HTTP状态码不对，只要进程在运行就认为服务可用
                    result["success"] = True
                    result["message"] = f"服务进程运行中（HTTP状态: {response.status_code}）"

            except ImportError:
                # 没有requests库，仅依靠进程检查
                result["success"] = True
                result["message"] = "服务进程运行中（跳过HTTP检查）"
            except Exception as e:
                # 网络错误，但进程运行，仍然认为服务可用
                result["success"] = True
                result["message"] = "服务进程运行中（网络检查失败）"
                self.logger.debug(f"网络检查失败但进程正常: {e}")

        except Exception as e:
            result["message"] = f"检查服务状态失败: {str(e)}"
            self.logger.error(f"测试连接异常: {e}")

        return result


if __name__ == "__main__":
    # 测试服务控制器
    import logging
    from config_manager import get_config

    logging.basicConfig(level=logging.INFO)

    config = get_config()
    controller = ServiceController(config)

    def status_callback(status, details):
        print(f"状态变化: {status}, 详情: {details}")

    controller.set_status_callback(status_callback)

    print("当前状态:", controller.get_service_status())
    print("测试连接:", controller.test_service_connection())